﻿/**
 *  The CLIK Label component is a noneditable standard textField wrapped by a MovieClip symbol, with a few additional convenient features. Internally, the Label supports the same properties and behaviors as the standard textField, however only a handful of commonly used features are exposed by the component itself. Access to the Label’s actual textField is provided if the user needs to change its properties directly. In certain cases, such as those described below, developers may use the standard textField instead of the Label component.
    
    Since the Label is a MovieClip symbol, it can be embellished with graphical elements, which is not possible with the standard textField. As a symbol, it does not need to be configured per instance like textField instances.  The Label also provides a disabled state that can be defined in the timeline. Whereas, complex AS2 code is required to mimic such behavior with the standard textField. 
    
    The Label component uses constraints by default, which means resizing a Label instance on the stage will have no visible effect at runtime. If resizing textFields is required, developers should use the standard textField instead of the Label in most cases. In general, if consistent reusability is not a requirement for the text element, the standard textField is a lighter weight alternative than the Label component.
    
   <b>Inspectable Properties</b>
        The inspectable properties of the Label component are:
        <ul>
            <li><i>autoSize</i>: Determines if the Label will scale to fit the text that it contains and which direction to align the resized button. Setting the autoSize property to {@code autoSize="none"} will leave its current size unchanged.</li>
            <li><i>enabled</i>: Disables the Label if set to false.</li>
            <li><i>text</i>: Sets the text of the Label.</li>
            <li><i>visible</i>: Hides the Label if set to false.</li>
        </ul>
    
    <b>States</b>
        The CLIK Label component supports two states based on the disabled property.
        <ul>
            <li>A default or enabled state.</li>
            <li>a disabled state.</li>
        </ul>
        
    <b>Events</b>
    All event callbacks receive a single Event parameter that contains relevant information about the event. The following properties are common to all events. <ul>
    <li><i>type</i>: The event type.</li>
    <li><i>target</i>: The target that generated the event.</li></ul>
        
    The events generated by the component are listed below. The properties listed next to the event are provided in addition to the common properties.
    <ul>
        <li><i>ComponentEvent.SHOW</i>: The visible property has been set to true at runtime.</li>
        <li><i>ComponentEvent.HIDE</i>: The visible property has been set to false at runtime.</li>
        <li><i>ComponentEvent.STATE_CHANGE</i>: The Label's state has changed.</li>
    </ul>
*/

/**************************************************************************

Filename    :   Label.as

Copyright   :   Copyright 2011 Autodesk, Inc. All Rights reserved.

Use of this software is subject to the terms of the Autodesk license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.

**************************************************************************/

package scaleform.clik.controls {
    
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    
    import scaleform.clik.constants.ConstrainMode;
    import scaleform.clik.constants.ControllerType;
    import scaleform.clik.constants.InputValue;
    import scaleform.clik.constants.InvalidationType;
    import scaleform.clik.constants.NavigationCode;
    import scaleform.clik.core.UIComponent;
    import scaleform.clik.data.DataProvider;
    import scaleform.clik.events.ButtonEvent;
    import scaleform.clik.events.ComponentEvent;
    import scaleform.clik.events.InputEvent;
    import scaleform.clik.interfaces.IDataProvider;
    import scaleform.clik.ui.InputDetails;
    import scaleform.clik.utils.ConstrainedElement;
    import scaleform.clik.utils.Constraints;
    import scaleform.gfx.FocusEventEx;
    import scaleform.gfx.MouseEventEx;
    
    [Event(name = "change", type = "flash.events.Event")]
    
    public class Label extends UIComponent {
        
    // Constants:
        
    // Public Properties:
        /** True if constraints are disabled for the component. Setting the disableConstraintsproperty to {@code disableConstraints=true} will remove constraints from the textfield. This is useful for components with timeline based textfield size tweens, since constraints break them due to a Flash quirk. */
        public var constraintsDisabled:Boolean;
        
    // Protected Properties:
        protected var _text:String, _autoSize:String = TextFieldAutoSize.NONE, isHtml:Boolean, state:String = "default", _newFrame:String;
        
    // UI Elements:
        /** A reference to the textField instance used to display the selected item's label. Note that when state changes are made, the textField instance may change, so changes made to it externally may be lost. */
        public var textField:TextField;
        
        override protected function preInitialize():void {
            if (!constraintsDisabled)	constraints = new Constraints(this, ConstrainMode.COUNTER_SCALE);
        }
        
    // Public getter / setters:
        [Inspectable(defaultValue="true")]
        override public function get enabled():Boolean { return super.enabled; }
        override public function set enabled(value:Boolean):void {
            if (value == super.enabled) { return; }
            super.enabled = value;
            mouseEnabled = mouseChildren = value;
            setState(defaultState);
        }
        
        /**
         * The text to be displayed by the Label component. This property assumes that localization has been 
         * handled externally.
         * @see #htmlText For formatted text, use the {@code htmlText} property.
         */
        [Inspectable(name="text")]
        public function get text():String { return _text; }
        public function set text(value:String):void {
            if (!value)	value == "";
            isHtml = false;
            _text = value;
            invalidateData();
        }
        
        /**
         * The html text to be displayed by the label component.  This property assumes that localization has 
         * been handled externally.
         * @see #text For plain text use {@code text} property.
         */
        public function get htmlText():String { return _text; }
        public function set htmlText(value:String):void {
			if (!value)	value == "";
            isHtml = true;
            _text = value;
            invalidateData();
        }
        
        /**
         * Determines if the component will scale to fit the text that it contains. Setting the {@code autoSize} 
         * property to {@code false} will leave its current size unchanged.
         */
        [Inspectable(defaultValue="none", enumeration="none,left,right,center")]
        public function get autoSize():String { return _autoSize; }
        public function set autoSize(value:String):void {
            if (value == _autoSize)	return;
            _autoSize = value;
            invalidateData();
        }
        
        public function get length():uint { return textField.length; }
        public function get defaultState():String {
            return (!enabled ? "disabled" : (focused ? "focused" : "default"));
        }
        
    // Public Methods:
        /** Append a String to the existing text within the Label. */
        public function appendText(value:String):void {
            _text += value;
            isHtml = false;
            invalidateData();
        }
        
        /** 
         * Append an HTML-formatted String to the existing text within the Label. Note that the Label will be 
         * automatically set to display HTML text.
         */
        public function appendHtml(value:String):void {
            _text += value;
            isHtml = true;
            invalidateData();
        }
        

        override protected function configUI():void {
            if (!constraintsDisabled)	constraints.addElement("textField", textField, Constraints.ALL);
            
            focusable = false;
            // setState(defaultState, "default"); // NFM: Not sure if this is required for a Label.
        }
        
        protected function calculateWidth():Number {
			var element:ConstrainedElement;
			
            if (!constraints || !textField)	return actualWidth;
            
            if (!constraintsDisabled)	element = constraints.getElement("textField");
            
            return Math.ceil(textField.textWidth + element.left + element.right + 5); // We add 5 pixels to accommodate Flash's poor measurement of anti-aliased fonts.
        }
        
        protected function alignForAutoSize():void {
            if (!initialized || _autoSize == TextFieldAutoSize.NONE || !textField)	return;
            
            var oldWidth:Number = _width;
            var newWidth:Number = _width = calculateWidth();
            
            switch (_autoSize) {
                case TextFieldAutoSize.RIGHT:
                    x += oldWidth - newWidth;
                    break;
                case TextFieldAutoSize.CENTER:
                    x += oldWidth * 0.5 - newWidth * 0.5;
                    break;
            }
        }
        
        override protected function draw():void {
            // State is invalid, and has been set (is not the default)
            if (isInvalid(InvalidationType.STATE)) {
                if (_newFrame) {
                    gotoAndPlay(_newFrame);
                    _newFrame = null;
                }
                
                updateAfterStateChange();
                dispatchEvent(new ComponentEvent(ComponentEvent.STATE_CHANGE));
                invalidate(InvalidationType.SIZE);
            } 
            if (isInvalid(InvalidationType.DATA)) {
                updateText();
                if (autoSize != TextFieldAutoSize.NONE) {
                    alignForAutoSize();
                    invalidateSize();
                }
            }
            
            // Resize and update constraints
            if (isInvalid(InvalidationType.SIZE)) {
                setActualSize(_width, _height);
                if (!constraintsDisabled) {
                    constraints.update(_width, _height);
                }
            }
        }
        
        protected function updateText():void {
            if (_text && textField) {
                if (isHtml)	textField.htmlText = _text;
                else	textField.text = _text;
            }
        }
        
        protected function updateAfterStateChange():void {
            if (!initialized)	return;
            if (constraints && !constraintsDisabled) {
                constraints.updateElement("textField", textField); // Update references in Constraints 
            }
            
            updateText();
            dispatchEvent(new ComponentEvent(ComponentEvent.STATE_CHANGE));
        }
        
        protected function setState(...states:Array):void {
			var l:uint = states.length, i:uint, _state:String;
			
            if (l == 1) {
				_state = states[0].toString();
                if (state != _state && _labelHash[_state]) { 
                    state = _newFrame = _state;
                    invalidateState();
                }
                return;
            }
            
            for (i=0; i<l; ++i) {
				_state = states[i].toString();
                if (_labelHash[_state]) {
                    state = _newFrame = _state;
                    invalidateState();
                    break;
                }
            }
        }
    }
}